package test.fairway;

import java.util.List;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.net.URL;

import com.amazonaws.HttpMethod;
import com.amazonaws.auth.BasicSessionCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.DeleteObjectRequest;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;

public class S3Client {

	private S3SessionCredential credential;
	private String bucket;
	private AmazonS3 s3;

	public S3Client(S3SessionCredential credential, String bucket) {
		this.credential = credential;
		this.bucket = bucket;

		BasicSessionCredentials basicSessionCredentials = new BasicSessionCredentials(credential.getAccessKeyId(),
				credential.getSecretAccessKey(), credential.getSessionToken());

		s3 = new AmazonS3Client(basicSessionCredentials);
	}

	public S3Client(S3SessionCredential credential, String bucket, String endpoint) {
		this(credential, bucket);
		s3.setEndpoint(endpoint);
	}

	private URL generatePresignedUrl(String objectKey, long duration, HttpMethod method) {
		Date expiration = DateUtil.getExpirationDate(duration);

		GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucket, objectKey);
		generatePresignedUrlRequest.setMethod(method);
		generatePresignedUrlRequest.setExpiration(expiration);
		URL url = s3.generatePresignedUrl(generatePresignedUrlRequest);
		return url;
	}

	public URL getDownloadUrl(String objectKey, long duration) {
		return generatePresignedUrl(objectKey, duration, HttpMethod.GET);
	}

	public URL getDownloadUrl(String objectKey) {
		return getDownloadUrl(objectKey, 3600);
	}

	public URL getDownloadUrl(String objectKey, java.util.Date expiration) {
		return getDownloadUrl(objectKey, DateUtil.getDurationSeconds(expiration));
	}

	public URL getUploadUrl(String objectKey, long duration) {
		return generatePresignedUrl(objectKey, duration, HttpMethod.PUT);
	}

	public URL getUploadUrl(String objectKey) {
		return getUploadUrl(objectKey, 3600);
	}

	public URL getUploadUrl(String objectKey, java.util.Date expiration) {
		return getUploadUrl(objectKey, DateUtil.getDurationSeconds(expiration));
	}

	public List<String> getObjectList() {
		ObjectListing objectListing = s3.listObjects(new ListObjectsRequest().withBucketName(bucket));
		List<String> objects = new ArrayList<String>();
        for (S3ObjectSummary objectSummary : objectListing.getObjectSummaries()) {
            objects.add(objectSummary.getKey());
        }
        return objects;
	}

	public Map<String, String> downloadObject(String objectKey, OutputStream output) throws IOException {
		S3Object object = s3.getObject(new GetObjectRequest(bucket, objectKey));
		copyStream(object.getObjectContent(), output);
		return object.getObjectMetadata().getUserMetadata();
	}

	public void uploadObject(String objectKey, File inputfile) {
		PutObjectRequest putRequest = new PutObjectRequest(bucket, objectKey, inputfile);
		s3.putObject(putRequest);
	}

	public void uploadObject(String objectKey, File inputfile, Map<String, String> metadatas) throws FileNotFoundException {
		if (metadatas == null) {
			uploadObject(objectKey, inputfile);
			return;
		}
		FileInputStream input = new FileInputStream(inputfile);
		uploadObject(objectKey, input, inputfile.length(), "application/octet-stream", metadatas);
	}

	public void uploadObject(String objectKey, InputStream input, long length, String contentType,
			Map<String, String> metadatas) {
		ObjectMetadata metadata = new ObjectMetadata();
		metadata.setContentLength(length);
		metadata.setContentType(contentType);
		if (metadatas != null)
			metadata.setUserMetadata(metadatas);
		PutObjectRequest putRequest = new PutObjectRequest(bucket, objectKey, input, metadata);
		s3.putObject(putRequest);
	}

	public void deleteObject(String objectKey) {
		s3.deleteObject(new DeleteObjectRequest(bucket, objectKey));
	}
	
	public static void copyStream(InputStream input, OutputStream output) throws IOException {
		byte[] buffer = new byte[4096];
		int bytesRead;
		while ((bytesRead = input.read(buffer)) != -1) {
			output.write(buffer, 0, bytesRead);
		}
	}
}
